package test.demo;

import tech.waterism.modelbase.PredictBase;
import tech.waterism.modelbase.PredictResult;
import com.alibaba.fastjson.JSON;
import test.demo.gamma.Gamma;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * 迟滞瞬时单位线
 *
 * @author 初京刚
 * @version 1.0
 * @date 2022-4-5
 */
public class LIUH extends PredictBase {

    /**
     * 流域基流
     */
    private double Qbasic;

    /**
     * 线性水库出流系数
     */
    private double K;

    /**
     * 线性水库个数
     */
    private double n;

    /**
     * 线性渠道滞时
     */
    private double T;

    /**
     * 降水时间
     */
    private Date[] floodTm;

    /**
     * 降水序列长度
     */
    private Integer floodRainRange;

    /**
     * 降水时段长
     */
    private double timeInterval;

    /**
     * 模拟总产流
     */
    private double[] runoffSim;

    public LIUH(Map<String, String> data) {
        super();
        Qbasic = Double.parseDouble(data.get("Qbasic"));
        floodTm = JSON.parseObject(data.get("dt"), Date[].class);               // input[i].data.dt
        runoffSim = JSON.parseObject(data.get("runoffSim"), double[].class);    // inout[i].data.runoffSim
        K = Double.parseDouble(data.get("K"));
        n = Double.parseDouble(data.get("n"));
        T = Double.parseDouble(data.get("T"));
        timeInterval = Double.parseDouble(data.get("clen"));                    // index.clen
        floodRainRange = runoffSim.length;
    }

    public double getTimeInterval() {
        return timeInterval;
    }

    /**
     * @param paramTimeInterval 降水时段长
     */
    public void setTimeInterval(double paramTimeInterval) {
        this.timeInterval = paramTimeInterval;
    }

    public double getQbasic() {
        return Qbasic;
    }

    public void setQbasic(double paramQbasic) {
        Qbasic = paramQbasic;
    }

    public double[] getRunoffSim() {
        return runoffSim;
    }

    public void setRunoffSim(double[] paramRunoffSim) {
        this.runoffSim = paramRunoffSim;
        floodRainRange = runoffSim.length;
    }

    public Date[] getFloodTm() {
        return floodTm;
    }

    /**
     * @param paramFloodTm 降水时间
     */
    public void setFloodTm(Date[] paramFloodTm) {
        this.floodTm = paramFloodTm;
    }

    /**
     * 模型计算
     */
    public Map<String, double[]> LIUHmodel() {

        // 迟滞瞬时单位线
        double[] QSim = LIUHLine();

        Map<String, double[]> simulationResult = new HashMap<>();

        simulationResult.put("runoffSim", runoffSim);
        simulationResult.put("QSim", QSim);
        return simulationResult;
    }

    /**
     * 迟滞瞬时单位线
     */
    private double[] LIUHLine() {
        double[] QSim = new double[floodRainRange];
        double denominator;
        double[] w = new double[floodRainRange];
        if (n == 0 || n == 1) {
            denominator = K;
            for (int t = 0; t < floodRainRange; t++) {
                if (t <= T) {
                    w[t] = 0.0;
                } else {
                    w[t] = 1 / denominator * Math.exp((-1) * (t - T) / K);
                }
            }
        } else {
            denominator = K * Gamma.gamma(n);
            for (int t = 0; t < floodRainRange; t++) {
                if (t <= n * T) {
                    w[t] = 0.0;
                } else {
                    w[t] = 1 / denominator * Math.exp((-1) * (t - n * T) / K) * Math.pow((t - n * T) / K, n - 1);
                }
            }
        }

        for (int i = 0; i < floodRainRange; i++) {
            for (int j = 0; j < floodRainRange; j++) {
                if (i + j < floodRainRange) {
                    QSim[j + i] = QSim[j + i] + runoffSim[i] * w[j];
                }
            }
        }

        for (int k = 0; k < floodRainRange; k++) {
            QSim[k] += Qbasic;
        }

        return QSim;
    }

    @Override
    public void init() {

    }

    @Override
    public PredictResult predict() {
        PredictResult result = new PredictResult();
        Map<String, double[]> map = LIUHmodel();

        //河道演进结果
        result.setQSim(map.get("QSim"));
        return result;
    }
}